#!/usr/bin/env ruby
#Universidad del Valle de Guatemala
#Sistemas Operativos Avanzados
#David Ytzen Hsieh Lo #08225

require 'socket'
require 'thread'
require 'monitor'
require 'rexml/document'
require 'logger'
require 'base64'

include REXML

#Load config xml file
file = File.new("./config.xml")
doc = Document.new(file)

#Parse config xml tree
root = doc.root
$baseDirPath = root.elements["baseDir"].text  
$baseDirPath.gsub!(/[\n]+/, ""); #replace \n with ''

$cacheSize = Integer(root.elements["cacheSize"].text)

#Add / if missing
if ($baseDirPath[$baseDirPath.length-1] != 47)
  $baseDirPath += "/"  
end

#REMOVE
puts $baseDirPath


#Create log
$logger = Logger.new("./server.log")
$logger.level = Logger::DEBUG
$logger.info  "\n::::::::::SERVER STARTED::::::::::\n"

#Create Cache hash
$cache = Hash.new
$cacheQueue = Array.new

#Load passwords
$passwords = Array.new
if(File.exist?($baseDirPath + "passwd"))
  File.open($baseDirPath + "passwd", 'r') { |f|
    
    while (line = f.gets)
    $passwords.push(line)
  end
  }
end



class HTTPServer
  
  @currentFileContent = Array.new
  def initialize(socket)
    
    #Get request
    request = socket.readline         
    puts request
    
    #Log request
    request.gsub!(/[\n]+/, "");
    @log =  "\n----------------------------------------------\n"
    @log << request + "\n"
    
    #Get fileName from request
    if request =~ /GET .* HTTP.*/
      fileName = request.gsub(/GET /, '').gsub(/ HTTP.*/, '')
    end
    
    #Delete first char if fileName starts with a /
    if fileName[0] == 47
      fileName = fileName.reverse.chop.reverse
    end
    
    fileName = $baseDirPath + fileName
    
    if(File.directory?(fileName))
      fileName = fileName + "index.html"
    end
    
    
    puts fileName
    
    validGET = request.match(/GET .* HTTP\/1\.1/)
    puts validGET



    #Get current time
    cTime = Time.now.to_a
    fCTime = Time.utc(*cTime)    
    
    
    #Log host
    #Parse the head info
    host = socket.readline
    head = host
    
    
    if(File.extname(fileName) == ".htmls") 
      puts File.extname(fileName)
      puts "Request auth"
      authorized = false
    else
      authorized = true
    end
    
    #Check left head info
    while(head.length > 2 )
      head = socket.readline
      
      puts head
      #Look for authorization in head
      if(head =~ /Authorization(.)*/)
        codedPass =  head.split[2]
        puts codedPass
        decodedPass = Base64::decode64(codedPass)
        puts decodedPass
        
        #Check password existence in file
        $passwords.each { |pass|

          puts pass
          puts decodedPass
          if(pass.strip == decodedPass.strip)
            authorized = true
            puts "ASDFGH"
          end
        }
      end
      
    end



    #Delete \n and spaces from host and log it
    host.chomp;
    @log << host + "\n"
    
    #Check for authorization
    unless (authorized)
      socket.puts "HTTP/1.1 401 Authorization Required\n"
      socket.puts "Server: HTTPd/1.0\n"
      socket.puts "Date: " + fCTime.inspect +  "\n"
      socket.puts "WWW-Authenticate: Basic realm='Secure Area'\n"
      socket.puts "Content-Type: text/html\n"
      socket.puts "Content-Length: 311\n"
    end


    #Check for request validity
    unless (validGET)
      puts "Bad request"
      socket.puts "HTTP/1.1 400 Bad Request\n"
      @log << "HTTP/1.1 400 Bad Request \n"
      socket.close
      next
    end
    
    #Get file basename
    file = File.basename(fileName)
    puts fileName
    
    #Check if file exists
    unless ( File.exists?(fileName) )
      puts "Not found"
      @log << "HTTP/1.1 404 File Not Found \n"
      @log << "Requested file: #{file} \n"
      socket.puts "HTTP/1.1 404 File Not Found\n"
      socket.puts "\n"
      socket.puts"File not found"

      

      socket.close
      next    
    end
    
    

    @log << "HTTP/1.1 200 OK \n"
    socket.puts "HTTP/1.1 200 OK\n"
    socket.puts "Connection: close\n"
    socket.puts "Server: IfritServer (Unix) (Debian/Linux)"
    socket.puts "Date:" + fCTime.inspect + "\n"

    
    fileType = getFileType(file)
    puts fileType
    socket.puts "Content-Type: "  + fileType + "\n"
    

    @log << "Requested file: #{file} \n"

    fileSize = File.size?(fileName)
    socket.puts "Content-Length:" + fileSize.to_s() + "\n\n"

    @log << fileSize.to_s() + " bytes transfered \n"
    
   
    #Check if file exists in cache
    if ($cache.has_key?(file)) 
      
      @currentFileContent = $cache[file]
      
      if($cacheQueue.length > 4)
        $cacheQueue.delete(file)
        $cacheQueue.compact
      end
      
      $cacheQueue.push(file)
      
      
      @log << "Read from cache \n"
      puts "DEL CACHE! JAJA"
        
    else #if the file is not in cache
      
      filePath = $baseDirPath + file;
      @log << "Read from file \n"
      
      @currentFileContent = Array.new

      

      #Open file and save contents to currentFileContent
      File.open(filePath, 'r') { |f|
        
          while (line = f.gets)
            @currentFileContent.push(line)
            
            #socket.puts line
          end
      }
      
      if($cacheQueue.length >= $cacheSize)
        #Delete the last used file on cache
        $cache.delete($cacheQueue[0])
        $cacheQueue.delete_at(0)     
          $cacheQueue.compact
      end
      $cacheQueue.push(file)
      $cache[file] = @currentFileContent
      
      
    end

    
    #print each line of the currentFileContent Array
    @currentFileContent.each {|li| socket.puts li}
   
    
    socket.close
    $logger.info @log
    
  end
  
  
  def getFileType(path)
    ext = File.extname(path)

    return "text/html"    if ext == ".html" or ext == ".htm" or ext == ".htmls"
    return "text/plain"   if ext == ".txt"
    return "text/css"     if ext == ".css"
    return "Image/jpeg"   if ext == ".jpeg" or ext == ".jpg"
    return "Image/gif"    if ext == ".gif"
    return "Image/bmp"    if ext == ".bmp"
    return "Image/png"    if ext == ".png"
    return "text/plain"   if ext == ".rb"
    return "text/xml"     if ext == ".xml"
    return "text/xml"     if ext == ".xsl"
    return "image/x-icon" if ext == ".ico"
    return "text/html"

  end

end

server = TCPServer.new('127.0.0.1', '81')

puts "----------------------------------------------"
puts "Starting Ifrit server..."
puts "----------------------------------------------"

File.open('./Logo.txt', 'r') do |f1|
  while line = f1.gets
    puts line
  end
end

puts "Server running..."
puts "----------------------------------------------"

threadPool = Array.new

while socket = server.accept
  Thread.start(socket) do |socket|
    HTTPServer.new(socket)
  end
end



